import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class Tetris extends JFrame implements KeyListener {
    // 俄罗斯方块的行数
    private static final int gameRow = 26;
    // 俄罗斯方块的列数
    private static final int gameCol = 12;
    // 文本域数组
    JTextArea text[][];
    // 值数组
    int data[][];

    // 游戏状态标签
    JLabel stateLabel;
    // 游戏得分标签
    JLabel scoreLabel;

    // 记录游戏状态
    boolean isRunning;

    // 所有的方块
    int[] blockArray;

    // 当前方块
    int curBlock;

    // 方块产生的位置
    int x;
    int y;

    // 方块在每一层暂停的时间
    int time = 1000;

    // 游戏得分
    int score = 0;

    // 游戏是否暂停
    boolean game_pause = false;

    // 初始化窗口类
    public void initWindow(){
        // 设置窗口的大小
        this.setSize(600, 750);

        // 设置窗口的可见性
        this.setVisible(true);

        // 设置窗口的位置为居中
        this.setLocationRelativeTo(null);

        // 设置释放窗口
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 设置窗口大小不可变
        this.setResizable(false);

        // 设置窗口标题
        this.setTitle("俄罗斯方块");
    }

    // 游戏区域界面初始化
    public void initGamePanel(){
        // 创建游戏区域面板
        JPanel gameMain = new JPanel();

        // 设置布局
        gameMain.setLayout(new GridLayout(gameRow, gameCol, 1,1));

        for (int i = 0; i < text.length; i++) {     // 行
            for(int j=0;j<text[i].length;j++){      // 列
                text[i][j] = new JTextArea(gameRow, gameCol);
                // 设置文本域的背景样式
                text[i][j].setBackground(Color.white);
                // 设置键盘事件
                text[i][j].addKeyListener(this);
                // 设置文本域不可编辑
                text[i][j].setEditable(false);
                // 设置边界
                if(j==0 || j==text[i].length-1 || i==text.length-1){
                    text[i][j].setBackground(Color.MAGENTA);
                    // 有方块
                    data[i][j] = 1;
                }

                // 将方块添加到游戏主面板
                gameMain.add(text[i][j]);
            }
        }

        // 设置窗口为border布局
        this.setLayout(new BorderLayout());
        // 将游戏主面板添加到窗口中
        this.add(gameMain, BorderLayout.CENTER);
    }

    // 创建游戏说明面板
    public void initExplainPanel(){
        // 创建说明面板
        JPanel explainLeft = new JPanel();
        JPanel explainRight = new JPanel();

        // 设置面板的布局方式
        explainLeft.setLayout(new GridLayout(4,1));
        explainRight.setLayout(new GridLayout(2,1));

        // 左侧说明面板加入说明标签
        explainLeft.add(new JLabel("按空格键，方块变形"));
        explainLeft.add(new JLabel("按左箭头，方块左移"));
        explainLeft.add(new JLabel("按右箭头，方块右移"));
        explainLeft.add(new JLabel("按下箭头，方块下移"));

        // 设置游戏得分标签的颜色
        scoreLabel.setForeground(Color.RED);

        // 右侧说明面板加入说明标签
        explainRight.add(stateLabel);
        explainRight.add(scoreLabel);

        // 将说明面板添加到游戏窗口中
        this.add(explainLeft, BorderLayout.WEST);
        this.add(explainRight, BorderLayout.EAST);
    }

    // 开始游戏
    public void game_begin(){
        while (true) {
            // 判断游戏是否结束
            if(!isRunning){
                break;
            }

            // 游戏运行
            game_run();
        }

        // 修改游戏状态标签
        stateLabel.setText("游戏状态：游戏结束！");
    }

    // 游戏运行
    public void game_run(){
        // 随机产生一个方块
        randBlock();

        // 设置方块位置
        x = 0;  // 行
        y = 5;  // 列

        // 遍历所有层
        for (int i = 0; i < gameRow; i++) {
            try {
                // 暂停1000ms
                Thread.sleep(time);

                if(game_pause){     // 游戏暂停
                    // 抵消每次循环的i++，保持i值不变
                    i++;
                }else{      // 游戏未暂停
                    // 判断方块是否能下落
                    if(!canFall(x,y)){
                        // 不能下落
                        // 将data置为1，表示有方块占用
                        changeData(x,y);

                        // 循环遍历4层，看是否有行可以消除
                        for(int j=x;j<x+4;j++){
                            int sum = 0;
                            for(int k=1;k<=(gameCol-2);k++){
                                if(data[j][k]==1){
                                    sum++;
                                }
                            }

                            if(sum==(gameCol-2)){
                                // 消除该行
                                removeRow(j);
                            }
                        }

                        // 判断游戏是否失败 - 最上面四层出现了方块代表失败
                        for(int j=1;j<=(gameCol-2);j++){
                            if(data[3][j]==1){
                                isRunning = false;
                                break;
                            }
                        }

                        break;
                    }else{
                        x++;    // 层数+1
                        // 执行下落1层
                        fall(x,y);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 消除对应行的方块
     * @param row 行
     */
    private void removeRow(int row) {
        // 请在下方写代码实现消除方块的功能
        int temp = 100;
        // 所有行数据下移
        for(int i=row;i>=1;i--){
            for(int j=1;j<=(gameCol-2);j++){
                data[i][j] = data[i-1][j];
            }
        }
        // 刷新游戏界面
        refresh(row);
        // 游戏加速
        if(time>temp){
            time -= temp;
        }
        // 更新得分
        score = score + temp;
        scoreLabel.setText("游戏得分："+score);

    }

    /**
     * 刷新游戏区域的方法
     * @param row
     */
    private void refresh(int row) {
        // 请在下方写代码实现刷新游戏区域的功能
        i--){
            for(int j=1;j<=(gameCol-2);j++){
                if(data[i][j]==1){
                    // 有方块
                    text[i][j].setBackground(Color.BLUE);
                }else{
                    // 没有方块
                    text[i][j].setBackground(Color.WHITE);
                }
            }
        }

    }

    /**
     * 将行、列对应的数据置为1
     * @param x 行
     * @param y 列
     */
    private void changeData(int x, int y) {
        // 定义一个变量
        int temp = 0x8000;
        // 遍历4行4列
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if((temp & curBlock) != 0){
                    // 有方块
                    data[x][y] = 1;
                }
                // 列改变
                y++;
                temp >>= 1;
            }
            // 下移一行判断
            x++;
            // 列回到初始位置
            y = y - 4;
        }

    }

    // 判断方块是否能下落
    public boolean canFall(int x, int y){
        // 定义一个变量
        int temp = 0x8000;

        // 遍历4行4列
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if((temp & curBlock) != 0){
                    // 有方块，需判断它的下一行是否有方块
                    if(data[x+1][y]==1){
                        // 有方块则不能下落
                        return false;
                    }
                }
                // 列改变
                y++;
                temp >>= 1;
            }
            // 下移一行判断
            x++;
            // 列回到初始位置
            y = y - 4;
        }

        return true;
    }

    // 执行下落
    public void fall(int x, int y){
        if(x>0){
            // 前清除上一行
            clearBlock(x-1, y);
        }

        // 绘制当前行
        drawBlock(x, y);
    }

    /**
     * 绘制该行列的俄罗斯方块
     * @param row 行
     * @param col 列
     */
    private void drawBlock(int row, int col) {
        int temp = 0x8000;

        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if((temp & curBlock)!=0){
                    // 将有颜色的方块清除
                    text[row][col].setBackground(Color.BLUE);
                }
                col++;
                temp >>= 1;
            }
            row++;
            col = col - 4;
        }
    }

    /**
     * 清除该行列的俄罗斯方块
     * @param row 行
     * @param col 列
     */
    private void clearBlock(int row, int col) {
        int temp = 0x8000;

        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if((temp & curBlock)!=0){
                    // 将有颜色的方块清除
                    text[row][col].setBackground(Color.WHITE);
                }
                col++;
                temp >>= 1;
            }
            row++;
            col = col - 4;
        }
    }

    /**
     * 随机生成方块
     */
    public void randBlock(){
        Random ran = new Random();
        curBlock = blockArray[ran.nextInt(22)];
    }

    // 无参构造方法
    public Tetris(){
        // 初始化文本域
        text = new JTextArea[gameRow][gameCol];
        // 初始化值域
        data = new int[gameRow][gameCol];

        // 初始化游戏状态标签和得分标签
        stateLabel = new JLabel("游戏状态：正在进行中");

        // 初始化游戏得分标签
        scoreLabel = new JLabel("游戏得分：0");

        // 初始化游戏界面
        initGamePanel();

        // 创建游戏说明面板
        initExplainPanel();

        // 初始化游戏窗口
        initWindow();

        // 设置游戏状态
        isRunning = true;

        // 初始化方块 - 一共有22个
        blockArray = new int[]{0x00cc,0x8888,0x000f,0x888f,0xf888,0xf111,0x111f,0x0eee,0xffff,0x0008,
        0x0888,0x000e,0x0088,0x000c,0x08c8,0x00e4,0x04c4,0x004e,0x08c4,0x006c,0x04c8,0x00c6};
    }

    public static void main(String[] args) {
        Tetris tetris = new Tetris();
        tetris.game_begin();
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // 按了暂停键
        if(e.getKeyChar() == 'p'){
            if(!isRunning){
                return;
            }

            // 改变游戏状态
            game_pause = !game_pause;

            // 改变游戏状态标签
            if(game_pause){
                stateLabel.setText("游戏状态：游戏暂停！");
            }else{
                stateLabel.setText("游戏状态：游戏进行中！");
            }
        }
        // 按空格键让方块变形
        if(e.getKeyChar()==KeyEvent.VK_SPACE){
            // 判断游戏是否结束
            if(!isRunning){
                return;
            }

            // 判断游戏是否暂停
            if(game_pause){
                return;
            }

            // 获得当前方块索引值
            int old=0;
            for (old = 0; old < blockArray.length; old++) {
                if(curBlock==blockArray[old]){
                    break;
                }
            }

            // 无需变形情况
            if(old==0 || old==7 || old==8 || old==9){
                return;
            }

            // 清除老的方块
            clearBlock(x, y);

            // 存储变形之后的变量
            int next;

            // 变形方块
            // 1<->2
            if(old==1 || old==2){
                next = blockArray[old==1?2:1];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 3 4 5 6 循环变换
            if(old>=3 && old<=6){
                next = blockArray[old+1>6?3:old+1];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 10<->11
            if(old==10 || old==11){
                next = blockArray[old==10?11:10];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 12<->13
            if(old==12 || old==13){
                next = blockArray[old==12?13:12];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 14 15 16 17 循环变换
            if(old>=14 && old<=17){
                next = blockArray[old+1>17?14:old+1];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 18<->19
            if(old==18 || old==19){
                next = blockArray[old==18?19:18];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 20<->21
            if(old==20 || old==21){
                next = blockArray[old==20?21:20];

                // 判断是否可以变形
                if(canTurn(next, x, y)){
                    curBlock = next;
                }
            }

            // 画出新的方块
            drawBlock(x, y);
        }

    }

    /**
     * 判断方块next在x,y位置是否可以变形
     * @param next 方块值
     * @param x 行
     * @param y 列
     * @return
     */
    private boolean canTurn(int next, int x, int y) {
        int temp = 0x8000;

        for (int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if((temp & next) != 0){
                    if(data[x][y]==1){
                        return false;
                    }
                }
                y++;
                temp >>= 1;
            }
            x++;
            y = y - 4;
        }
        return true;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // 按了键盘的左箭头
        if(e.getKeyCode()==37){
            // 判断游戏状态
            if(!isRunning){
                // 游戏已经停止
                return;
            }

            // 判断游戏是否暂停
            if(game_pause){
                return;
            }

            // 判断是否碰到了左墙壁
            if(y<=1){
                // 碰到了左墙壁
                return;
            }

            // 判断左边是否有方块
            int temp = 0x8000;
            for(int i=x;i<x+4;i++){
                for(int j=y;j<y+4;j++){
                    if((temp & curBlock) != 0){
                        // 看其左边是否有方块
                        if(data[i][j-1]==1){
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            // 可以左移
            // 消除原来位置的方块
            clearBlock(x, y);

            y--;

            // 重绘新位置的方块
            drawBlock(x, y);
        }

        // 按了右箭头
        if(e.getKeyCode()==39){
            // 判断游戏运行状态
            if(!isRunning){
                return;
            }

            // 判断游戏是否暂停
            if(game_pause){
                return;
            }

            // 判断是否碰到右墙壁
            int temp = 0x8000;
            int m = x;  // 行
            int n = y;  // 列
            int num = 1;    // 记录最右边格子坐标

            for(int i=0;i<4;i++){
                for(int j=0;j<4;j++){
                    if((temp & curBlock)!=0){
                        if(n>num){
                            num = n;
                        }
                    }

                    n++;
                    temp >>= 1;
                }
                m++;
                n = n -4;
            }

            if(num>=(gameCol-2)){
                // 碰到右墙壁
                return;
            }

            // 判断是否碰到右边的方块
            temp = 0x8000;
            for(int i=x;i<x+4;i++){
                for(int j=y;j<y+4;j++){
                    if((temp & curBlock)!=0){
                        // 看其右边是否有方块
                        if(data[i][j+1]==1){
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            // 可以右移
            clearBlock(x, y);

            y++;

            drawBlock(x, y);
        }

        // 按了下箭头
        if(e.getKeyCode()==40){
            // 判断运行状态
            if(!isRunning){
                return;
            }

            // 判断游戏是否暂停
            if(game_pause){
                return;
            }

            // 判断是否可以下落
            if(!canFall(x,y)){
                return;
            }

            // 可以下落
            clearBlock(x, y);
            x++;
            drawBlock(x,y);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}
